﻿using Microsoft.Data.SqlClient;

using PLCS.Domain.Configs;
using PLCS.Domain.Orders;
using PLCS.Domain.Processes;
using PLCS.Domain.Shared.Processes;
using PLCS.Domain.Solutions;
using PLCS.EquipmentService.ParsersHelpers;
using PLCS.Shared.Common.Constant;

namespace PLCS.EquipmentService.BackgroundWorks.Analysis;

[DisallowConcurrentExecution]
internal class WorkOrderAnalysisWorker : QuartzBackgroundWorkerBase
{
    private readonly ISolutionRepository _solutionRepository;
    private readonly IOrderRepository _orderRepository;
    private readonly IProcessRepository _processRepository;
    private readonly PathAnalyzer dataAnalyzer;
    private readonly IConfiguration _configuration;
    private readonly IRepository<OrderRunningCondition> _orderRunningConditionRepository;
    private readonly IBackDataRepository _backDataRepository;

    public WorkOrderAnalysisWorker(ISolutionRepository solutionRepository,
                                   IOrderRepository orderRepository,
                                   IConfiguration configuration,
                                   IRepository<OrderRunningCondition> orderRunningConditionRepository,
                                   IProcessRepository processRepository,
                                   PathAnalyzer dataAnalyzer,
                                   IBackDataRepository backDataRepository)
    {
        _solutionRepository = solutionRepository;
        _orderRepository = orderRepository;
        _processRepository = processRepository;
        this.dataAnalyzer = dataAnalyzer;
        _configuration = configuration;
        _orderRunningConditionRepository = orderRunningConditionRepository;
        JobDetail = JobBuilder.Create<WorkOrderAnalysisWorker>().WithIdentity(nameof(WorkOrderAnalysisWorker)).Build();
        Trigger = TriggerBuilder.Create().WithIdentity(nameof(WorkOrderAnalysisWorker))
            .WithSimpleSchedule(s => s.WithIntervalInSeconds(1)
            .RepeatForever()
            .WithMisfireHandlingInstructionIgnoreMisfires())
            .Build();

        ScheduleJob = async scheduler =>
        {
            if (!await scheduler.CheckExists(JobDetail.Key) && LocalConfigManager.IsWorkOrderAnalysis)
            {
                await scheduler.ScheduleJob(JobDetail, Trigger);
            }
        };
        _backDataRepository = backDataRepository;
    }

    /// <summary>
    /// 工作执行方法
    /// </summary>
    /// <param name="context"></param>
    /// <returns></returns>
    public override async Task Execute(IJobExecutionContext context)
    {
        // 找到所有未解析完的订单
        var orders = await GetNeedAnalysisOrders();

        foreach (var order in orders.Where(x => !string.IsNullOrEmpty(x.SolutionNo)))
        {
            if (order.OrderType == Domain.Shared.Orders.OrderType.加工工单)
            {
                AnalysisProcessingWorkOrder(order);
                continue;
            }

            if (order?.OrderRunningConditions?.Where(x => x.ProcessStatus != ProcessStatus.已完成).Count() > 0)
                continue;

            // 找到对应的方案
            var solution = await _solutionRepository.GetSolutionByNoAsync(order.SolutionNo);
            if (solution == null || solution.SolutionSteps == null)
                continue;

            SolutionStep solutionStep = null;

            if (order.Status == OrderStatus.未解析)
            {
                // 因为是未解析的工单，说明现在是第一步，找到第一个工步
                solutionStep = solution.SolutionSteps.OrderBy(x => x.StepOrder).First();
                order.ChangeStatus(OrderStatus.解析中);
            }
            else
            {
                solutionStep = await FindStepByOrder(solution, order);
            }
            if (solutionStep == null) continue;

            // 开始解析步骤的最终工序
            var step = await AnalysisFinalProcess(solutionStep);

            // 如果解析完，还是空，那么这个工单可能要停了，不过一般不存在这样的情况。
            if (string.IsNullOrEmpty(step.FinalProcess)) continue;

            var process = await _processRepository.FindAsync(x => x.ProcessNo == step.FinalProcess);
            if (process == null) continue;

            // 在工单的运行情况表里插入一条数据
            AddOrderRunningCondition(step.FinalProcess, SetStepOrder(order), order, process.IsEnd);
            await _orderRepository.UpdateAsync(order);
        }
    }

    /// <summary>
    /// 解析加工工单
    /// </summary>
    /// <param name="order"></param>
    public void AnalysisProcessingWorkOrder(Order order)
    {
        var materials = order.TrayNo.Split(ConstantChar.SplitChar);
    }

    /// <summary>
    /// 获取所有未解析完成的工单
    /// </summary>
    /// <returns></returns>
    private async Task<List<Order>> GetNeedAnalysisOrders()
    {
        // 找到所有未解析的订单
        var orders = await _orderRepository.GetListAsync(x => x.Status == OrderStatus.未解析 || x.Status == OrderStatus.解析中);
        var res = new List<Order>();
        foreach (var order in orders)
        {
            var orderRunningCondition = await _orderRunningConditionRepository.GetListAsync(x => x.OrderNo == order.OrderNo /*&& x.ProcessStatus != Domain.Shared.Enums.TaskStatus.执行完成*/);
            if (orderRunningCondition != null && orderRunningCondition.Count != 0)
            {
                order.OrderRunningConditions = orderRunningCondition;
            }
            res.Add(order);
        }

        return res;
    }

    /// <summary>
    /// 添加一条工单工序运行情况
    /// </summary>
    /// <param name="processNo"></param>
    /// <param name="whichStep"></param>
    /// <param name="order"></param>
    private void AddOrderRunningCondition(string processNo, int whichStep, Order order, YesNo isEnd)
    {
        lock (this)
        {
            var orderRunningConditions = _orderRunningConditionRepository.GetListAsync(x => x.OrderNo == order.OrderNo).Result;
            if (!orderRunningConditions.Select(x => x.WhichStep).Contains(whichStep))
                _orderRunningConditionRepository.InsertAsync(new OrderRunningCondition(processNo, whichStep, order, isEnd)).Wait();
        }
    }

    /// <summary>
    ///  设置第几条工序的序号
    /// </summary>
    /// <param name="order"></param>
    /// <returns></returns>
    public int SetStepOrder(Order order)
    {
        var orderRunningConditions = order.OrderRunningConditions;
        if (orderRunningConditions == null) return 1;
        return orderRunningConditions.Count + 1;
    }

    /// <summary>
    /// 通过工单和方案，找到下一步步骤
    /// </summary>
    /// <param name="solution"></param>
    /// <param name="order"></param>
    /// <returns></returns>
    private async Task<SolutionStep> FindStepByOrder(Solution solution, Order order)
    {
        var orderRunningConditions = await _orderRunningConditionRepository.GetListAsync(x => x.OrderNo == order.OrderNo);
        var lastOrderRunningCondition = orderRunningConditions.OrderByDescending(x => x.CreationTime).First();

        string finalProcess = lastOrderRunningCondition.ProcessNo;
        // 判断工序有没有承接工序的
        var process = await _processRepository.GetAsync(x => x.ProcessNo == lastOrderRunningCondition.ProcessNo);
        if (!string.IsNullOrWhiteSpace(process.ContinueProcess))
            finalProcess = process.ContinueProcess;

        var solutionStep = FindStepByProcess(solution, finalProcess);
        int stepOrder = int.Parse(solutionStep.StepOrder) + 1;

        var laststep = solution.SolutionSteps.FirstOrDefault(x => x.StepOrder == stepOrder.ToString());
        return laststep;
    }

    /// <summary>
    /// 在一个方案下通过工序找到步骤
    /// </summary>
    /// <returns></returns>
    private SolutionStep FindStepByProcess(Solution solution, string processNo)
    {
        return solution.SolutionSteps?.FirstOrDefault(step => step.Process.Contains(processNo));
    }

    /// <summary>
    /// 解析步骤的最终工序
    /// </summary>
    /// <param name="solutionStep"></param>
    /// <returns></returns>
    private async Task<SolutionStep> AnalysisFinalProcess(SolutionStep solutionStep)
    {
        if (solutionStep == null)
            return null;
        // 如果工序列表为空，直接返回（一般不存在这样的情况，但还是要判断下~）
        if (solutionStep.Processes == null)
            return solutionStep;

        // 如果最终工序不为空，那就不用解析了~直接返回
        if (!string.IsNullOrEmpty(solutionStep.FinalProcess))
            return solutionStep;

        // 开始轮询步骤下的所有工序
        foreach (var process in solutionStep.Processes)
        {
            // 如果工序的触发条件为空，那么可以直接返回这个工序为最终工序了
            if (process.Triggers == null)
            {
                solutionStep.FinalProcess = process.ProcessNo;
                return solutionStep;
            }

            // 开始解析工序的触发条件是否满足
            bool isTriggerAllValid = true;
            foreach (var trigger in process.Triggers)
            {
                // 一条不满足就可以直接break了，解析下一个工序
                if (!await CheckTrigger(trigger))
                {
                    isTriggerAllValid = false;
                    break;
                }
            }
            if (isTriggerAllValid)
            {
                // 如果所有触发条件都满足，可以返回这个工序为最终工序了
                solutionStep.FinalProcess = process.ProcessNo;
                return solutionStep;
            }
        }
        // 如果没有一个工序的触发条件通，
        // 默认直接选择最后一个工序最为最终工序
        solutionStep.FinalProcess = solutionStep.Processes.Last().ProcessNo;
        return solutionStep;
    }

    /// <summary>
    /// 判断触发条件是否满足
    /// </summary>
    /// <param name="trigger"></param>
    /// <returns></returns>
    private async Task<bool> CheckTrigger(Trigger trigger)
    {
        string? value = string.Empty;

        if (trigger.IsReadFromPLC)
        {
            var isReadOK = PLCReader.ReadFromPlc(trigger.IpAddress, trigger.DBNumber, trigger.Offset, trigger.DataType, trigger.Size, out value);
            if (!isReadOK)
                return false;
        }
        else
        {
            value = await _backDataRepository.GetBackDataByKeyAsync(trigger.BackDataKey, trigger.BackDataPath);
        }
        if (string.IsNullOrEmpty(value))
            return false;
        switch (trigger.ComparisonType)
        {
            case ComparisonType.等于:
                return trigger.CompareValue.ToLower().Equals(value.ToLower());

            case ComparisonType.不等于:
                return !trigger.CompareValue.ToLower().Equals(value.ToLower());

            case ComparisonType.大于:
                int comValue, currentValue;
                var comValueIsInt = int.TryParse(trigger.CompareValue, out comValue);
                if (!comValueIsInt) return false;
                var currentValueIsInt = int.TryParse(value, out currentValue);
                if (!currentValueIsInt) return false;

                return currentValue > comValue;

            case ComparisonType.小于:
                int comValue2, currentValue2;
                var comValueIsInt2 = int.TryParse(trigger.CompareValue, out comValue2);
                if (!comValueIsInt2) return false;
                var currentValueIsInt2 = int.TryParse(value, out currentValue2);
                if (!currentValueIsInt2) return false;

                return currentValue2 < comValue2;

            case ComparisonType.包含:
                return value.ToLower().Contains(trigger.CompareValue.ToLower());

            case ComparisonType.不包含:
                return !value.ToLower().Contains(trigger.CompareValue.ToLower());

            default:
                return false;
        }
    }
}